﻿/*
 * Copyright (C) 
 * Copyright (C) 
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* ScriptData
SDName: Bosses_Opera
SD%Complete: 90
SDComment: Oz, Hood, and RAJ event implemented. RAJ event requires more testing.
SDCategory: Karazhan
EndScriptData */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "karazhan.h"
#include "Player.h"
#include "SpellInfo.h"
#pragma execution_character_set("utf-8")

/***********************************/
/*** OPERA WIZARD OF OZ EVENT *****/
/*********************************/

#define SAY_DOROTHEE_DEATH          "啊，我终于……终于可以回家了."
#define SAY_DOROTHEE_SUMMON         "多多，你不会让这些家伙伤害我们的，对吧？"
#define SAY_DOROTHEE_TITO_DEATH     "多多，哦不！你们杀了托托."
#define SAY_DOROTHEE_AGGRO          "哦，多多，我们必须找到回家的路，那位老巫师是我们唯一的希望了，稻草人，狮子还有铁皮人，你们能不能..啊有人来了."

#define SAY_ROAR_AGGRO              "我不是害怕你!你想要战斗吗?啊，你是吗?来! 我将把两支爪子放在背后跟你战斗!"
#define SAY_ROAR_DEATH              "你不用这么做!"
#define SAY_ROAR_SLAY               "去睡一小会吧！"

#define SAY_STRAWMAN_AGGRO          "我该把你怎么办？我完全不知道."
#define SAY_STRAWMAN_DEATH          "别让他们把我做成草垫."
#define SAY_STRAWMAN_SLAY           "看来我并不是一个失败品."

#define SAY_TINHEAD_AGGRO           "我真的能使用心.嘿，我能有你的心吗?"
#define SAY_TINHEAD_DEATH           "继续当个老铁锈."
#define SAY_TINHEAD_SLAY            "看来我还没有锈成一滩费铁."
#define EMOTE_RUST                  "%s开始生锈!"

#define SAY_CRONE_AGGRO             "我为你们感到可悲小家伙们，啊哈哈哈哈哈哈哈……"
#define SAY_CRONE_AGGRO2            "很快就结束啦！啊哈哈哈哈哈哈哈……"
#define SAY_CRONE_DEATH             "怎么可能，唔..这真是个残酷的世界."
#define SAY_CRONE_SLAY              "搞定你了对不对？啊哈哈哈哈哈哈哈……"

/**** Spells ****/
// Dorothee
#define SPELL_WATERBOLT         31012
#define SPELL_SCREAM            31013
#define SPELL_SUMMONTITO        31014

// Tito
#define SPELL_YIPPING           31015

// Strawman
#define SPELL_BRAIN_BASH        31046
#define SPELL_BRAIN_WIPE        31069
#define SPELL_BURNING_STRAW     31075

// Tinhead
#define SPELL_CLEAVE            31043
#define SPELL_RUST              31086

// Roar
#define SPELL_MANGLE            31041
#define SPELL_SHRED             31042
#define SPELL_FRIGHTENED_SCREAM 31013

// Crone
#define SPELL_CHAIN_LIGHTNING   32337

// Cyclone
#define SPELL_KNOCKBACK         32334
#define SPELL_CYCLONE_VISUAL    32332

/** Creature Entries **/
#define CREATURE_TITO           17548
#define CREATURE_CYCLONE        18412
#define CREATURE_CRONE          18168

void SummonCroneIfReady(InstanceScript* instance, Creature* creature)
{
	instance->SetData(DATA_OPERA_OZ_DEATHCOUNT, SPECIAL);  // Increment DeathCount

	if (instance->GetData(DATA_OPERA_OZ_DEATHCOUNT) == 4)
	{
		if (Creature* pCrone = creature->SummonCreature(CREATURE_CRONE, -10891.96f, -1755.95f, creature->GetPositionZ(), 4.64f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR * 2 * IN_MILLISECONDS))
		{
			if (creature->GetVictim())
				pCrone->AI()->AttackStart(creature->GetVictim());
		}
	}
};

class boss_dorothee : public CreatureScript
{
public:
	boss_dorothee() : CreatureScript("boss_dorothee") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_dorotheeAI>(creature);
	}

struct boss_dorotheeAI : public ScriptedAI
{
	boss_dorotheeAI(Creature* c) : ScriptedAI(c)
	{
		instance = c->GetInstanceScript();
	}

	InstanceScript* instance;

	uint32 AggroTimer;

	uint32 WaterBoltTimer;
	uint32 FearTimer;
	uint32 SummonTitoTimer;

	bool SummonedTito;
	bool TitoDied;

	void Reset() override
	{
		AggroTimer = 500;

		WaterBoltTimer = 5000;
		FearTimer = 15000;
		SummonTitoTimer = 47500;

		SummonedTito = false;
		TitoDied = false;

		if (instance)
			instance->SetData(TYPE_OPERA, NOT_STARTED);

		if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
		{
			go->SetGoState(GO_STATE_READY);
		}
	}

	void EnterCombat(Unit* /*who*/) override
	{
		me->MonsterYell(SAY_DOROTHEE_AGGRO, LANG_UNIVERSAL, NULL);

		if (instance)
		{
			instance->SetData(TYPE_OPERA, IN_PROGRESS);
		}
	}

	void JustReachedHome() override
	{
		me->DespawnOrUnsummon();
	}

	void SummonTito();                                      // See below

	void JustDied(Unit* /*killer*/) override
	{
		me->MonsterYell(SAY_DOROTHEE_DEATH, LANG_UNIVERSAL, NULL);

		SummonCroneIfReady(instance, me);
	}

	void AttackStart(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::AttackStart(who);
	}

	void MoveInLineOfSight(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::MoveInLineOfSight(who);
	}

	void UpdateAI(uint32 diff) override
	{
		if (AggroTimer)
		{
			if (AggroTimer <= diff)
			{
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
				AggroTimer = 0;
			}
			else AggroTimer -= diff;
		}

		if (!UpdateVictim())
			return;

		if (WaterBoltTimer <= diff)
		{
			DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_WATERBOLT);
			WaterBoltTimer = TitoDied ? 1500 : 5000;
		}
		else WaterBoltTimer -= diff;

		if (FearTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_SCREAM);
			FearTimer = 30000;
		}
		else FearTimer -= diff;

		if (!SummonedTito)
		{
			if (SummonTitoTimer <= diff)
				SummonTito();
			else SummonTitoTimer -= diff;
		}

		DoMeleeAttackIfReady();
	}
  };
};

class npc_tito : public CreatureScript
{
public:
	npc_tito() : CreatureScript("npc_tito") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return new npc_titoAI(creature);
	}

	struct npc_titoAI : public ScriptedAI
{
	npc_titoAI(Creature* c) : ScriptedAI(c) {}

	uint64 DorotheeGUID;
	uint32 YipTimer;

	void Reset()
	{
		DorotheeGUID = 0;
		YipTimer = 10000;
	}

	void EnterCombat(Unit* /*who*/) override {}

	void JustDied(Unit* /*killer*/) override
	{
		if (DorotheeGUID)
		{
			Creature* Dorothee = (ObjectAccessor::GetCreature((*me), DorotheeGUID));
			if (Dorothee && Dorothee->IsAlive())
			{
				CAST_AI(boss_dorothee::boss_dorotheeAI, Dorothee->AI())->TitoDied = true;
				me->MonsterYell(SAY_DOROTHEE_TITO_DEATH, LANG_UNIVERSAL, Dorothee);
			}
		}
	}

	void UpdateAI(uint32 diff) override
	{
		if (!UpdateVictim())
			return;

		if (YipTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_YIPPING);
			YipTimer = 10000;
		}
		else YipTimer -= diff;

		DoMeleeAttackIfReady();
	}
  };
};

void boss_dorothee::boss_dorotheeAI::SummonTito()
{
	if (Creature* pTito = me->SummonCreature(CREATURE_TITO, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
	{
		me->MonsterYell(SAY_DOROTHEE_SUMMON, LANG_UNIVERSAL, NULL);
		CAST_AI(npc_tito::npc_titoAI, pTito->AI())->DorotheeGUID = me->GetGUID();
		pTito->AI()->AttackStart(me->GetVictim());
		SummonedTito = true;
		TitoDied = false;
	}
}

class boss_strawman : public CreatureScript
{
public:
	boss_strawman() : CreatureScript("boss_strawman") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_strawmanAI>(creature);
	}

struct boss_strawmanAI : public ScriptedAI
{
	boss_strawmanAI(Creature* c) : ScriptedAI(c)
	{
		instance = c->GetInstanceScript();
	}

	InstanceScript* instance;

	uint32 AggroTimer;
	uint32 BrainBashTimer;
	uint32 BrainWipeTimer;

	void Reset() override
	{
		AggroTimer = 13000;
		BrainBashTimer = 5000;
		BrainWipeTimer = 7000;

		if (instance)
			instance->SetData(TYPE_OPERA, NOT_STARTED);

		if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
		{
			go->SetGoState(GO_STATE_READY);
		}
	}

	void AttackStart(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::AttackStart(who);
	}

	void MoveInLineOfSight(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::MoveInLineOfSight(who);
	}

	void EnterCombat(Unit* /*who*/) override
	{
		me->MonsterYell(SAY_STRAWMAN_AGGRO, LANG_UNIVERSAL, NULL);

		if (instance)
		{
			instance->SetData(TYPE_OPERA, IN_PROGRESS);
		}
	}

	void JustReachedHome() override
	{
		me->DespawnOrUnsummon();
	}

	void SpellHit(Unit* /*caster*/, const SpellInfo* Spell) override
	{
		if ((Spell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) && (!(rand() % 10)))
			DoCast(me, SPELL_BURNING_STRAW, true);
	}

	void JustDied(Unit* /*killer*/) override
	{
		me->MonsterYell(SAY_STRAWMAN_DEATH, LANG_UNIVERSAL, NULL);

		SummonCroneIfReady(instance, me);
	}

	void KilledUnit(Unit* /*victim*/) override
	{
		me->MonsterYell(SAY_STRAWMAN_SLAY, LANG_UNIVERSAL, NULL);
	}

	void UpdateAI(uint32 diff) override
	{
		if (AggroTimer)
		{
			if (AggroTimer <= diff)
			{
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
				AggroTimer = 0;
			}
			else AggroTimer -= diff;
		}

		if (!UpdateVictim())
			return;

		if (BrainBashTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_BRAIN_BASH);
			BrainBashTimer = 15000;
		}
		else BrainBashTimer -= diff;

		if (BrainWipeTimer <= diff)
		{
			if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
				DoCast(pTarget, SPELL_BRAIN_WIPE);
			BrainWipeTimer = 20000;
		}
		else BrainWipeTimer -= diff;

		DoMeleeAttackIfReady();
	}
  };
};

class boss_tinhead : public CreatureScript
{
public:
	boss_tinhead() : CreatureScript("boss_tinhead") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_tinheadAI>(creature);
	}

struct boss_tinheadAI : public ScriptedAI
{
	boss_tinheadAI(Creature* c) : ScriptedAI(c)
	{
		instance = c->GetInstanceScript();
	}

	InstanceScript* instance;

	uint32 AggroTimer;
	uint32 CleaveTimer;
	uint32 RustTimer;

	uint8 RustCount;

	void Reset() override
	{
		AggroTimer = 15000;
		CleaveTimer = 5000;
		RustTimer = 30000;

		RustCount = 0;

		if (instance)
			instance->SetData(TYPE_OPERA, NOT_STARTED);

		if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
		{
			go->SetGoState(GO_STATE_READY);
		}
	}

	void EnterCombat(Unit* /*who*/) override
	{
		me->MonsterYell(SAY_TINHEAD_AGGRO, LANG_UNIVERSAL, NULL);

		if (instance)
		{
			instance->SetData(TYPE_OPERA, IN_PROGRESS);
		}
	}

	void JustReachedHome() override
	{
		me->DespawnOrUnsummon();
	}

	void AttackStart(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::AttackStart(who);
	}

	void MoveInLineOfSight(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::MoveInLineOfSight(who);
	}

	void JustDied(Unit* /*killer*/) override
	{
		me->MonsterYell(SAY_TINHEAD_DEATH, LANG_UNIVERSAL, NULL);

		SummonCroneIfReady(instance, me);
	}

	void KilledUnit(Unit* /*victim*/) override
	{
		me->MonsterYell(SAY_TINHEAD_SLAY, LANG_UNIVERSAL, NULL);
	}

	void UpdateAI(uint32 diff) override
	{
		if (AggroTimer)
		{
			if (AggroTimer <= diff)
			{
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
				AggroTimer = 0;
			}
			else AggroTimer -= diff;
		}

		if (!UpdateVictim())
			return;

		if (CleaveTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_CLEAVE);
			CleaveTimer = 5000;
		}
		else CleaveTimer -= diff;

		if (RustCount < 8)
		{
			if (RustTimer <= diff)
			{
				++RustCount;
				me->MonsterYell(EMOTE_RUST, LANG_UNIVERSAL, NULL);
				DoCast(me, SPELL_RUST);
				RustTimer = 6000;
			}
			else RustTimer -= diff;
		}

		DoMeleeAttackIfReady();
	}
  };
};

class boss_roar : public CreatureScript
{
public:
	boss_roar() : CreatureScript("boss_roar") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_roarAI>(creature);
	}

struct boss_roarAI : public ScriptedAI
{
	boss_roarAI(Creature* c) : ScriptedAI(c)
	{
		instance = c->GetInstanceScript();
	}

	InstanceScript* instance;

	uint32 AggroTimer;
	uint32 MangleTimer;
	uint32 ShredTimer;
	uint32 ScreamTimer;

	void Reset() override
	{
		AggroTimer = 20000;
		MangleTimer = 5000;
		ShredTimer = 10000;
		ScreamTimer = 15000;

		if (instance)
			instance->SetData(TYPE_OPERA, NOT_STARTED);

		if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
		{
			go->SetGoState(GO_STATE_READY);
		}
	}

	void MoveInLineOfSight(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::MoveInLineOfSight(who);
	}

	void AttackStart(Unit* who) override
	{
		if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
			return;

		ScriptedAI::AttackStart(who);
	}

	void EnterCombat(Unit* /*who*/) override
	{
		me->MonsterYell(SAY_ROAR_AGGRO, LANG_UNIVERSAL, NULL);

		if (instance)
		{
			instance->SetData(TYPE_OPERA, IN_PROGRESS);
		}
	}

	void JustReachedHome() override
	{
		me->DespawnOrUnsummon();
	}

	void JustDied(Unit* /*killer*/) override
	{
		me->MonsterYell(SAY_ROAR_DEATH, LANG_UNIVERSAL, NULL);

		SummonCroneIfReady(instance, me);
	}

	void KilledUnit(Unit* /*victim*/) override
	{
		me->MonsterYell(SAY_ROAR_SLAY, LANG_UNIVERSAL, NULL);
	}

	void UpdateAI(uint32 diff) override
	{
		if (AggroTimer)
		{
			if (AggroTimer <= diff)
			{
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
				AggroTimer = 0;
			}
			else AggroTimer -= diff;
		}

		if (!UpdateVictim())
			return;

		if (MangleTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_MANGLE);
			MangleTimer = urand(5000, 8000);
		}
		else MangleTimer -= diff;

		if (ShredTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_SHRED);
			ShredTimer = urand(10000, 15000);
		}
		else ShredTimer -= diff;

		if (ScreamTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_FRIGHTENED_SCREAM);
			ScreamTimer = urand(20000, 30000);
		}
		else ScreamTimer -= diff;

		DoMeleeAttackIfReady();
	}
  };
};

static const float afCycloneSpawnLoc[4] = { -10907.68f, -1778.651f, 90.56018f, 0.61f };

class boss_crone : public CreatureScript
{
public:
	boss_crone() : CreatureScript("boss_crone") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_croneAI>(creature);
	}

	struct boss_croneAI : public ScriptedAI
	{
		boss_croneAI(Creature* creature) : ScriptedAI(creature)
		{
			instance = creature->GetInstanceScript();
		}

		InstanceScript* instance;

		uint32 CycloneTimer;
		uint32 ChainLightningTimer;

		void Reset() override
		{
			CycloneTimer = 30000;
			ChainLightningTimer = 10000;
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);

			if (instance)
				instance->SetData(TYPE_OPERA, NOT_STARTED);

			if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
			{
				go->SetGoState(GO_STATE_READY);
			}
		}

		void JustReachedHome() override
		{
			if (instance)
				instance->SetData(TYPE_OPERA, FAIL);
			me->DespawnOrUnsummon();
		}

		void KilledUnit(Unit* /*victim*/) override
		{
			me->MonsterYell(SAY_CRONE_SLAY, LANG_UNIVERSAL, NULL);
		}

		void EnterCombat(Unit* /*who*/) override
		{
			if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
			{
				go->SetGoState(GO_STATE_ACTIVE);
			}

			me->MonsterYell(SAY_CRONE_AGGRO, LANG_UNIVERSAL, NULL);

			if (instance)
			{
				instance->SetData(TYPE_OPERA, IN_PROGRESS);
			}
		}

		void JustDied(Unit* /*killer*/) override
		{
			me->MonsterYell(SAY_CRONE_DEATH, LANG_UNIVERSAL, NULL);

			instance->SetData(TYPE_OPERA, DONE);
			instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORLEFT), true);
			instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORRIGHT), true);

			if (GameObject* pSideEntrance = instance->instance->GetGameObject(instance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
				pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
		}

		void JustSummoned(Creature* pSummoned) override
		{
			pSummoned->CastSpell(pSummoned, SPELL_KNOCKBACK, true);
			pSummoned->CastSpell(pSummoned, SPELL_CYCLONE_VISUAL, true);
			pSummoned->GetMotionMaster()->MovePoint(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 15.0f);
		}

		void UpdateAI(uint32 diff) override
		{
			if (!UpdateVictim())
				return;

			if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
				me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);

			if (CycloneTimer <= diff)
			{
				if (Creature* Cyclone = DoSpawnCreature(CREATURE_CYCLONE, float(urand(0, 9)), float(urand(0, 9)), 0, 0, TEMPSUMMON_TIMED_DESPAWN, 15000))
					Cyclone->CastSpell(Cyclone, SPELL_CYCLONE_VISUAL, true);
				CycloneTimer = 30000;
			}
			else CycloneTimer -= diff;

			if (ChainLightningTimer <= diff)
			{
				DoCastVictim(SPELL_CHAIN_LIGHTNING);
				ChainLightningTimer = 15000;
			}
			else ChainLightningTimer -= diff;

			DoMeleeAttackIfReady();
		}
	};
};

class npc_cyclone : public CreatureScript
{
public:
	npc_cyclone() : CreatureScript("npc_cyclone") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return new npc_cycloneAI(creature);
	}

	struct npc_cycloneAI : public ScriptedAI
{
	npc_cycloneAI(Creature* c) : ScriptedAI(c) {}

	uint32 MoveTimer;

	void Reset() override
	{
		MoveTimer = 1000;
	}

	void EnterCombat(Unit* /*who*/) {}

	void MoveInLineOfSight(Unit* /*who*/)
	{
	}

	void UpdateAI(uint32 diff) override
	{
		if (!me->HasAura(SPELL_KNOCKBACK, 0))
			DoCast(me, SPELL_KNOCKBACK, true);

		if (MoveTimer <= diff)
		{
			Position pos;
			me->GetRandomNearPosition(pos, 10);
			me->GetMotionMaster()->MovePoint(0, pos);
			MoveTimer = urand(5000, 8000);
		}
		else MoveTimer -= diff;
	}
  };
};

/**************************************/
/**** Opera Red Riding Hood Event ****/
/************************************/

/**** Yells for the Wolf ****/
#define SAY_WOLF_AGGRO                  "可以一口把你吃掉！"
#define SAY_WOLF_SLAY                   "嗯…… 好吃！"
#define SAY_WOLF_HOOD                   "快跑吧小姑娘，快跑！"
#define SOUND_WOLF_DEATH                9275                //Only sound on death, no text.

/**** Spells For The Wolf ****/
#define SPELL_LITTLE_RED_RIDING_HOOD    30768
#define SPELL_TERRIFYING_HOWL           30752
#define SPELL_WIDE_SWIPE                30761

#define GOSSIP_GRANDMA          "喔, 奶奶, 你的耳朵怎么这么尖?"
#define GOSSIP_GRANDMA2         "喔, 奶奶, 你的眼睛怎么这么大?"
#define GOSSIP_GRANDMA3         "喔, 奶奶, 你的衣服怎么这么紫?"

/**** The Wolf's Entry ****/
#define CREATURE_BIG_BAD_WOLF           17521

class npc_grandmother : public CreatureScript
{
public:
	npc_grandmother() : CreatureScript("npc_grandmother") { }

	bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action)
	{
		player->PlayerTalkClass->ClearMenus();
		if (action == GOSSIP_ACTION_INFO_DEF + 1339)
		{
			if (Creature* pBigBadWolf = creature->SummonCreature(CREATURE_BIG_BAD_WOLF, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR * 2 * IN_MILLISECONDS))
				pBigBadWolf->AI()->AttackStart(player);

			creature->DespawnOrUnsummon();
		}

		switch (action)
		{
		case GOSSIP_ACTION_INFO_DEF + 1337:
			player->PlayerTalkClass->ClearMenus();
			player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_GRANDMA2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1338);
			player->SEND_GOSSIP_MENU(9000, creature->GetGUID());
			break;
		case GOSSIP_ACTION_INFO_DEF + 1338:
			player->PlayerTalkClass->ClearMenus();
			player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_GRANDMA3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1339);
			player->SEND_GOSSIP_MENU(9001, creature->GetGUID());
			break;

			player->CLOSE_GOSSIP_MENU();
		}

		return true;
	}

	bool OnGossipHello(Player* player, Creature* creature) override
	{
		player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_GRANDMA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1337);
		player->SEND_GOSSIP_MENU(8990, creature->GetGUID());

		return true;
	}
};

class boss_bigbadwolf : public CreatureScript
{
public:
	boss_bigbadwolf() : CreatureScript("boss_bigbadwolf") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_bigbadwolfAI>(creature);
	}

struct boss_bigbadwolfAI : public ScriptedAI
{
	boss_bigbadwolfAI(Creature* c) : ScriptedAI(c)
	{
		instance = c->GetInstanceScript();
	}

	InstanceScript* instance;

	uint32 ChaseTimer;
	uint32 FearTimer;
	uint32 SwipeTimer;

	uint64 HoodGUID;
	float TempThreat;

	bool IsChasing;

	void Reset() override
	{
		ChaseTimer = 30000;
		FearTimer = 25000 + rand() % 10000;
		SwipeTimer = 5000;

		HoodGUID = 0;
		TempThreat = 0;

		IsChasing = false;

		if (instance)
			instance->SetData(TYPE_OPERA, NOT_STARTED);

		if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
		{
			go->SetGoState(GO_STATE_READY);
		}
	}

	void EnterCombat(Unit* /*who*/) override
	{
		if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
		{
			go->SetGoState(GO_STATE_ACTIVE);
		}

		me->MonsterYell(SAY_WOLF_AGGRO, LANG_UNIVERSAL, NULL);

		if (instance)
		{
			instance->SetData(TYPE_OPERA, IN_PROGRESS);
		}
	}

	void JustReachedHome() override
	{
		me->DespawnOrUnsummon();
	}

	void JustDied(Unit* /*killer*/) override
	{
		DoPlaySoundToSet(me, SOUND_WOLF_DEATH);

		if (instance)
		{
			instance->SetData(TYPE_OPERA, DONE);
			instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORLEFT), true);
			instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORRIGHT), true);

			if (GameObject* pSideEntrance = instance->instance->GetGameObject(instance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
				pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
		}
	}

	void UpdateAI(uint32 diff) override
	{
		if (!UpdateVictim())
			return;

		DoMeleeAttackIfReady();

		if (ChaseTimer <= diff)
		{
			if (!IsChasing)
			{
				if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
				{
					me->MonsterYell(SAY_WOLF_HOOD, LANG_UNIVERSAL, NULL);
					DoCast(pTarget, SPELL_LITTLE_RED_RIDING_HOOD, true);
					TempThreat = DoGetThreat(pTarget);
					if (TempThreat)
						DoModifyThreatPercent(pTarget, -100);
					HoodGUID = pTarget->GetGUID();
					me->AddThreat(pTarget, 1000000.0f);
					ChaseTimer = 20000;
					IsChasing = true;
				}
			}
			else
			{
				IsChasing = false;

				if (Unit *pTarget = ObjectAccessor::GetUnit((*me), HoodGUID))
				{
					HoodGUID = 0;
					if (DoGetThreat(pTarget))
						DoModifyThreatPercent(pTarget, -100);
					me->AddThreat(pTarget, TempThreat);
					TempThreat = 0;
				}

				ChaseTimer = 40000;
			}
		}
		else ChaseTimer -= diff;

		if (IsChasing)
			return;

		if (FearTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_TERRIFYING_HOWL);
			FearTimer = urand(25000, 35000);
		}
		else FearTimer -= diff;

		if (SwipeTimer <= diff)
		{
			DoCast(me->GetVictim(), SPELL_WIDE_SWIPE);
			SwipeTimer = urand(25000, 30000);
		}
		else SwipeTimer -= diff;
	}
  };
};

/**********************************************/
/******** Opera Romeo and Juliet Event *******/
/********************************************/

/**** Speech *****/
#define SAY_JULIANNE_AGGRO              "你是个什么魔鬼？这样煎熬着我？"
#define SAY_JULIANNE_ENTER              "我的夫君呢？我的罗密欧呢？"
#define SAY_JULIANNE_DEATH01            "罗密欧，我来了，哦，我为你干了这一杯."
#define SAY_JULIANNE_DEATH02            "啊，好刀子！这就是你的鞘子，你刺了进去，让我死了吧！"
#define SAY_JULIANNE_RESURRECT          "来吧，可爱的黑魇的夜，把我的罗密欧给我！"
#define SAY_JULIANNE_SLAY               "离别是这样甜蜜的凄清."

#define SAY_ROMULO_AGGRO                "你要激怒我吗？那就来吧！"
#define SAY_ROMULO_DEATH                "你杀了我……却洋洋得意……"
#define SAY_ROMULO_ENTER                "今天这一场意外的变故怕要引起日后的灾祸."
#define SAY_ROMULO_RESURRECT            "无情的泥土，我要劈开你的馋吻！"
#define SAY_ROMULO_SLAY                 "现在我又重新得到了多大的安慰."

/***** Spells For Julianne *****/
#define SPELL_BLINDING_PASSION          30890
#define SPELL_DEVOTION                  30887
#define SPELL_ETERNAL_AFFECTION         30878
#define SPELL_POWERFUL_ATTRACTION       30889
#define SPELL_DRINK_POISON              30907

/***** Spells For Romulo ****/
#define SPELL_BACKWARD_LUNGE            30815
#define SPELL_DARING                    30841
#define SPELL_DEADLY_SWATHE             30817
#define SPELL_POISON_THRUST             30822

/**** Other Misc. Spells ****/
#define SPELL_UNDYING_LOVE              30951
#define SPELL_RES_VISUAL                24171

/*** Misc. Information ****/
#define CREATURE_ROMULO                 17533
#define ROMULO_X                        -10900
#define ROMULO_Y                        -1758


enum RAJPhase
{
	PHASE_JULIANNE = 0,
	PHASE_ROMULO = 1,
	PHASE_BOTH = 2,
};

void PretendToDie(Creature* creature)
{
	creature->InterruptNonMeleeSpells(true);
	creature->RemoveAllAuras();
	creature->SetHealth(0);
	creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
	creature->GetMotionMaster()->MovementExpired(false);
	creature->GetMotionMaster()->MoveIdle();
	creature->SetStandState(UNIT_STAND_STATE_DEAD);
}

void Resurrect(Creature* target)
{
	target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
	target->SetFullHealth();
	target->SetStandState(UNIT_STAND_STATE_STAND);
	target->CastSpell(target, SPELL_RES_VISUAL, true);
	if (target->GetVictim())
	{
		target->GetMotionMaster()->MoveChase(target->GetVictim());
		target->AI()->AttackStart(target->GetVictim());
	}
	else
		target->GetMotionMaster()->Initialize();
}

class boss_julianne : public CreatureScript
{
public:
	boss_julianne() : CreatureScript("boss_julianne") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_julianneAI>(creature);
	}

	struct boss_julianneAI : public ScriptedAI
	{
		boss_julianneAI(Creature* c) : ScriptedAI(c)
		{
			RomuloGUID = 0;
			Phase = PHASE_JULIANNE;

			BlindingPassionTimer = 30000;
			DevotionTimer = 15000;
			EternalAffectionTimer = 25000;
			PowerfulAttractionTimer = 5000;
			SummonRomuloTimer = 10000;
			DrinkPoisonTimer = 0;
			ResurrectSelfTimer = 0;
			SummonedRomulo = false;
			RomuloDead = false;

			instance = c->GetInstanceScript();
			EntryYellTimer = 1000;
			AggroYellTimer = 10000;
			IsFakingDeath = false;
			ResurrectTimer = 0;
		}

		InstanceScript* instance;

		uint32 EntryYellTimer;
		uint32 AggroYellTimer;

		uint64 RomuloGUID;

		uint32 Phase;

		uint32 BlindingPassionTimer;
		uint32 DevotionTimer;
		uint32 EternalAffectionTimer;
		uint32 PowerfulAttractionTimer;
		uint32 SummonRomuloTimer;
		uint32 ResurrectTimer;
		uint32 DrinkPoisonTimer;
		uint32 ResurrectSelfTimer;

		bool IsFakingDeath;
		bool SummonedRomulo;
		bool RomuloDead;

		void Reset()
		{
			RomuloGUID = 0;
			Phase = PHASE_JULIANNE;

			BlindingPassionTimer = 30000;
			DevotionTimer = 15000;
			EternalAffectionTimer = 25000;
			PowerfulAttractionTimer = 5000;
			SummonRomuloTimer = 10000;
			DrinkPoisonTimer = 0;
			ResurrectSelfTimer = 0;

			if (instance)
				instance->SetData(TYPE_OPERA, NOT_STARTED);

			if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
			{
				go->SetGoState(GO_STATE_READY);
			}

			if (IsFakingDeath)
			{
				Resurrect(me);
				IsFakingDeath = false;
			}

			SummonedRomulo = false;
			RomuloDead = false;
		}

		void EnterCombat(Unit* /*who*/) override
		{
			if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
			{
				go->SetGoState(GO_STATE_ACTIVE);
			}

			if (instance)
			{
				instance->SetData(TYPE_OPERA, IN_PROGRESS);
			}
		}

		void AttackStart(Unit* who) override
		{
			if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
				return;

			ScriptedAI::AttackStart(who);
		}

		void MoveInLineOfSight(Unit* who) override
		{
			if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
				return;

			ScriptedAI::MoveInLineOfSight(who);
		}

		void JustReachedHome() override
		{
			me->DespawnOrUnsummon();
		}

		void SpellHit(Unit* /*caster*/, const SpellInfo* Spell) override
		{
			if (Spell->Id == SPELL_DRINK_POISON)
			{
				me->MonsterYell(SAY_JULIANNE_DEATH01, LANG_UNIVERSAL, NULL);
				DrinkPoisonTimer = 2500;
			}
		}

		void DamageTaken(Unit* done_by, uint32 &damage, DamageEffectType, SpellSchoolMask) override;

		void JustDied(Unit* /*killer*/) override
		{
			me->MonsterYell(SAY_JULIANNE_DEATH02, LANG_UNIVERSAL, NULL);

				instance->SetData(TYPE_OPERA, DONE);
				instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORLEFT), true);
				instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORRIGHT), true);
				if (GameObject* pSideEntrance = instance->instance->GetGameObject(instance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
					pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
		}

		void KilledUnit(Unit* /*victim*/) override
		{
			me->MonsterYell(SAY_JULIANNE_SLAY, LANG_UNIVERSAL, NULL);
		}

		void UpdateAI(uint32 diff) override;
	};
};

class boss_romulo : public CreatureScript
{
public:
	boss_romulo() : CreatureScript("boss_romulo") { }

	CreatureAI* GetAI(Creature* creature) const
	{
		return GetInstanceAI<boss_romuloAI>(creature);
	}

	struct boss_romuloAI : public ScriptedAI
	{
		boss_romuloAI(Creature* c) : ScriptedAI(c)
		{
			JulianneGUID = 0;
			Phase = PHASE_ROMULO;

			BackwardLungeTimer = 15000;
			DaringTimer = 20000;
			DeadlySwatheTimer = 25000;
			PoisonThrustTimer = 10000;
			ResurrectTimer = 10000;

			IsFakingDeath = false;
			JulianneDead = true;

			instance = c->GetInstanceScript();
			EntryYellTimer = 8000;
			AggroYellTimer = 15000;
		}

		InstanceScript* instance;

		uint64 JulianneGUID;
		uint32 Phase;

		uint32 EntryYellTimer;
		uint32 AggroYellTimer;
		uint32 BackwardLungeTimer;
		uint32 DaringTimer;
		uint32 DeadlySwatheTimer;
		uint32 PoisonThrustTimer;
		uint32 ResurrectTimer;

		bool IsFakingDeath;
		bool JulianneDead;

		void Reset() override
		{
			JulianneGUID = 0;
			Phase = PHASE_ROMULO;

			BackwardLungeTimer = 15000;
			DaringTimer = 20000;
			DeadlySwatheTimer = 25000;
			PoisonThrustTimer = 10000;
			ResurrectTimer = 10000;

			IsFakingDeath = false;
			JulianneDead = true;

			if (instance)
				instance->SetData(TYPE_OPERA, NOT_STARTED);

			if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
			{
				go->SetGoState(GO_STATE_READY);
			}
		}

		void JustReachedHome() override
		{
			me->DespawnOrUnsummon();
		}

		void DamageTaken(Unit*, uint32 &damage, DamageEffectType, SpellSchoolMask) override
		{
			if (damage < me->GetHealth())
				return;

			//anything below only used if incoming damage will kill

			if (Phase == PHASE_ROMULO)
			{
				me->MonsterYell(SAY_ROMULO_DEATH, LANG_UNIVERSAL, NULL);
				PretendToDie(me);
				IsFakingDeath = true;
				Phase = PHASE_BOTH;

				if (Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID)))
				{
					CAST_AI(boss_julianne::boss_julianneAI, Julianne->AI())->RomuloDead = true;
					CAST_AI(boss_julianne::boss_julianneAI, Julianne->AI())->ResurrectSelfTimer = 10000;
				}

				damage = 0;
				return;
			}

			if (Phase == PHASE_BOTH)
			{
				if (JulianneDead)
				{
					if (Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID)))
					{
						Julianne->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
						Julianne->GetMotionMaster()->Clear();
						Julianne->setDeathState(JUST_DIED);
						Julianne->CombatStop(true);
						Julianne->DeleteThreatList();
						Julianne->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
					}
					return;
				}

				if (Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID)))
				{
					PretendToDie(me);
					IsFakingDeath = true;
					CAST_AI(boss_julianne::boss_julianneAI, Julianne->AI())->ResurrectTimer = 10000;
					CAST_AI(boss_julianne::boss_julianneAI, Julianne->AI())->RomuloDead = true;
					damage = 0;
					return;
				}
			}

			sLog->outError("scripts", "boss_romuloAI: DamageTaken reach end of code, that should not happen.");
		}

		void EnterCombat(Unit* /*who*/) override
		{
			if (GameObject* go = me->GetMap()->GetGameObject(instance->GetData64(DATA_GO_CURTAINS)))
			{
				go->SetGoState(GO_STATE_ACTIVE);
			}

			me->MonsterYell(SAY_ROMULO_AGGRO, LANG_UNIVERSAL, NULL);
			if (JulianneGUID)
			{
				Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID));
				if (Julianne && Julianne->GetVictim())
				{
					me->AddThreat(Julianne->GetVictim(), 1.0f);
					AttackStart(Julianne->GetVictim());
				}
			}

			if (instance)
			{
				instance->SetData(TYPE_OPERA, IN_PROGRESS);
			}
		}

		void MoveInLineOfSight(Unit* who) override
		{
			if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
				return;

			ScriptedAI::MoveInLineOfSight(who);
		}

		void JustDied(Unit* /*killer*/) override
		{
			me->MonsterYell(SAY_ROMULO_DEATH, LANG_UNIVERSAL, NULL);

				instance->SetData(TYPE_OPERA, DONE);
				instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORLEFT), true);
				instance->HandleGameObject(instance->GetData64(DATA_GO_STAGEDOORRIGHT), true);

				if (GameObject* pSideEntrance = instance->instance->GetGameObject(instance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
					pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
		}

		void KilledUnit(Unit* /*victim*/) override
		{
			me->MonsterYell(SAY_ROMULO_SLAY, LANG_UNIVERSAL, NULL);
		}

		void UpdateAI(uint32 diff) override
		{
			if (!UpdateVictim() || IsFakingDeath)
				return;

			if (JulianneDead)
			{
				if (ResurrectTimer <= diff)
				{
					Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID));
					if (Julianne && CAST_AI(boss_julianne::boss_julianneAI, Julianne->AI())->IsFakingDeath)
					{
						me->MonsterYell(SAY_ROMULO_RESURRECT, LANG_UNIVERSAL, NULL);
						Resurrect(Julianne);
						CAST_AI(boss_julianne::boss_julianneAI, Julianne->AI())->IsFakingDeath = false;
						JulianneDead = false;
						ResurrectTimer = 10000;
					}
				}
				else ResurrectTimer -= diff;
			}

			if (BackwardLungeTimer <= diff)
			{
				Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true);
				if (target && !me->HasInArc(float(M_PI), target))
				{
					DoCast(target, SPELL_BACKWARD_LUNGE);
					BackwardLungeTimer = urand(15000, 30000);
				}
			}
			else BackwardLungeTimer -= diff;

			if (DaringTimer <= diff)
			{
				DoCast(me, SPELL_DARING);
				DaringTimer = urand(20000, 40000);
			}
			else DaringTimer -= diff;

			if (DeadlySwatheTimer <= diff)
			{
				if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
					DoCast(target, SPELL_DEADLY_SWATHE);
				DeadlySwatheTimer = urand(15000, 25000);
			}
			else DeadlySwatheTimer -= diff;

			if (PoisonThrustTimer <= diff)
			{
				DoCastVictim(SPELL_POISON_THRUST);
				PoisonThrustTimer = urand(10000, 20000);
			}
			else PoisonThrustTimer -= diff;

			DoMeleeAttackIfReady();
		}
	};
};

void boss_julianne::boss_julianneAI::DamageTaken(Unit* /*done_by*/, uint32 &damage, DamageEffectType, SpellSchoolMask)
{
	if (damage < me->GetHealth())
		return;

	//anything below only used if incoming damage will kill

	if (Phase == PHASE_JULIANNE)
	{
		damage = 0;

		//this means already drinking, so return
		if (IsFakingDeath)
			return;

		me->InterruptNonMeleeSpells(true);
		DoCast(me, SPELL_DRINK_POISON);

		IsFakingDeath = true;
		//IS THIS USEFULL? Creature* Julianne = (Unit::GetCreature((*me), JulianneGUID));
		return;
	}

	if (Phase == PHASE_ROMULO)
	{
		//sLog->outError("TSCR: boss_julianneAI: cannot take damage in PHASE_ROMULO, why was i here?");
		damage = 0;
		return;
	}

	if (Phase == PHASE_BOTH)
	{
		//if this is true then we have to kill romulo too
		if (RomuloDead)
		{
			if (Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID)))
			{
				Romulo->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
				Romulo->GetMotionMaster()->Clear();
				Romulo->setDeathState(JUST_DIED);
				Romulo->CombatStop(true);
				Romulo->DeleteThreatList();
				Romulo->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
			}

			return;
		}

		//if not already returned, then romulo is alive and we can pretend die
		if (Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID)))
		{
			PretendToDie(me);
			IsFakingDeath = true;
			CAST_AI(boss_romulo::boss_romuloAI, Romulo->AI())->ResurrectTimer = 10000;
			CAST_AI(boss_romulo::boss_romuloAI, Romulo->AI())->JulianneDead = true;
			damage = 0;
			return;
		}
	}
	sLog->outError("TSCR: boss_julianneAI: DamageTaken reach end of code, that should not happen.");
}

void boss_julianne::boss_julianneAI::UpdateAI(uint32 diff)
{
	if (EntryYellTimer)
	{
		if (EntryYellTimer <= diff)
		{
			me->MonsterYell(SAY_JULIANNE_ENTER, LANG_UNIVERSAL, NULL);
			EntryYellTimer = 0;
		}
		else EntryYellTimer -= diff;
	}

	if (AggroYellTimer)
	{
		if (AggroYellTimer <= diff)
		{
			me->MonsterYell(SAY_JULIANNE_AGGRO, LANG_UNIVERSAL, NULL);
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			me->setFaction(16);
			AggroYellTimer = 0;
		}
		else AggroYellTimer -= diff;
	}

	if (DrinkPoisonTimer)
	{
		//will do this 2secs after spell hit. this is time to display visual as expected
		if (DrinkPoisonTimer <= diff)
		{
			PretendToDie(me);
			Phase = PHASE_ROMULO;
			SummonRomuloTimer = 10000;
			DrinkPoisonTimer = 0;
		}
		else DrinkPoisonTimer -= diff;
	}

	if (Phase == PHASE_ROMULO && !SummonedRomulo)
	{
		if (SummonRomuloTimer <= diff)
		{
			if (Creature* pRomulo = me->SummonCreature(CREATURE_ROMULO, ROMULO_X, ROMULO_Y, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR * 2 * IN_MILLISECONDS))
			{
				RomuloGUID = pRomulo->GetGUID();
				CAST_AI(boss_romulo::boss_romuloAI, pRomulo->AI())->JulianneGUID = me->GetGUID();
				CAST_AI(boss_romulo::boss_romuloAI, pRomulo->AI())->Phase = PHASE_ROMULO;
				DoZoneInCombat(pRomulo);

				pRomulo->setFaction(16);
			}
			SummonedRomulo = true;
		}
		else SummonRomuloTimer -= diff;
	}

	if (ResurrectSelfTimer)
	{
		if (ResurrectSelfTimer <= diff)
		{
			Resurrect(me);
			Phase = PHASE_BOTH;
			IsFakingDeath = false;

			if (me->GetVictim())
				AttackStart(me->GetVictim());

			ResurrectSelfTimer = 0;
			ResurrectTimer = 1000;
		}
		else ResurrectSelfTimer -= diff;
	}

	if (!UpdateVictim() || IsFakingDeath)
		return;

	if (RomuloDead)
	{
		if (ResurrectTimer <= diff)
		{
			Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID));
			if (Romulo && CAST_AI(boss_romulo::boss_romuloAI, Romulo->AI())->IsFakingDeath)
			{
				me->MonsterYell(SAY_JULIANNE_RESURRECT, LANG_UNIVERSAL, NULL);
				Resurrect(Romulo);
				CAST_AI(boss_romulo::boss_romuloAI, Romulo->AI())->IsFakingDeath = false;
				RomuloDead = false;
				ResurrectTimer = 10000;
			}
		}
		else ResurrectTimer -= diff;
	}

	if (BlindingPassionTimer <= diff)
	{
		if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
			DoCast(pTarget, SPELL_BLINDING_PASSION);
		BlindingPassionTimer = urand(30000, 45000);
	}
	else BlindingPassionTimer -= diff;

	if (DevotionTimer <= diff)
	{
		DoCast(me, SPELL_DEVOTION);
		DevotionTimer = urand(15000, 45000);
	}
	else DevotionTimer -= diff;

	if (PowerfulAttractionTimer <= diff)
	{
		DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_POWERFUL_ATTRACTION);
		PowerfulAttractionTimer = urand(5000, 30000);
	}
	else PowerfulAttractionTimer -= diff;

	if (EternalAffectionTimer <= diff)
	{
		if (urand(0, 1) && SummonedRomulo)
		{
			Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID));
			if (Romulo && Romulo->IsAlive() && !RomuloDead)
				DoCast(Romulo, SPELL_ETERNAL_AFFECTION);
		}
		else DoCast(me, SPELL_ETERNAL_AFFECTION);

		EternalAffectionTimer = urand(45000, 60000);
	}
	else EternalAffectionTimer -= diff;

	DoMeleeAttackIfReady();
}

void AddSC_bosses_opera()
{
    new boss_dorothee();
    new boss_strawman();
    new boss_tinhead();
    new boss_roar();
    new boss_crone();
    new npc_tito();
    new npc_cyclone();
	new npc_grandmother();
    new boss_bigbadwolf();
    new boss_julianne();
    new boss_romulo();
}
